Allow windows to be dragged by clicking on empty areas
authorMatthias Clasen <mclasen@redhat.com>
Fri, 16 Jul 2010 05:15:47 +0000 (01:15 -0400)
committerMatthias Clasen <mclasen@redhat.com>
Fri, 16 Jul 2010 05:15:47 +0000 (01:15 -0400)
Allow windows to be dragged by clicking on empty areas in menubars
and toolbars. This is under theme control, via the GtkWidget::window-dragging
style property. The idea is that it makes sense to turn this on if a
theme makes the window frame and the menubar/toolbar appear visually
contiguous.

The main patch was written by Cody Russell, with a contribution by
Ayan George. See bug 611313.

gtk/gtkmenushell.c
gtk/gtkseparatortoolitem.c
gtk/gtktoolbar.c
gtk/gtkwidget.c

index 1a8b80d686a2b75e01a739f4bbb9cd33c8a53c76..3fb8ee6a9610f13c3b86750d4c7ec652dd73c108 100644 (file)
@@ -596,18 +596,45 @@ gtk_menu_shell_button_press (GtkWidget      *widget,
 
   if (!menu_shell->active || !menu_shell->button)
     {
-      _gtk_menu_shell_activate (menu_shell);
+      gboolean initially_active = menu_shell->active;
 
       menu_shell->button = event->button;
 
-      if (menu_item && _gtk_menu_item_is_selectable (menu_item) &&
-         menu_item->parent == widget &&
-          menu_item != menu_shell->active_menu_item)
+      if (menu_item)
         {
-          if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement == GTK_TOP_BOTTOM)
+          if (_gtk_menu_item_is_selectable (menu_item) &&
+              menu_item->parent == widget &&
+              menu_item != menu_shell->active_menu_item)
             {
-              menu_shell->activate_time = event->time;
-              gtk_menu_shell_select_item (menu_shell, menu_item);
+              _gtk_menu_shell_activate (menu_shell);
+              menu_shell->button = event->button;
+
+              if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement == GTK_TOP_BOTTOM)
+                {
+                  menu_shell->activate_time = event->time;
+                  gtk_menu_shell_select_item (menu_shell, menu_item);
+                }
+            }
+        }
+      else
+        {
+          if (!initially_active)
+            {
+              gboolean window_drag = FALSE;
+
+              gtk_widget_style_get (widget,
+                                    "window-dragging", &window_drag,
+                                    NULL);
+
+              if (window_drag)
+                {
+                  gtk_menu_shell_deactivate (menu_shell);
+                  gtk_window_begin_move_drag (GTK_WINDOW (gtk_widget_get_toplevel (widget)),
+                                              event->button,
+                                              event->x_root,
+                                              event->y_root,
+                                              event->time);
+                }
             }
         }
     }
index 54f72dfbe4d374c5a753412361b566b6e02942ed..3b02eb20cada6859f7b26db0069ddf1961d116ba 100644 (file)
  * Use gtk_separator_tool_item_new() to create a new #GtkSeparatorToolItem.
  */
 
-
 #define MENU_ID "gtk-separator-tool-item-menu-id"
 
 struct _GtkSeparatorToolItemPrivate
 {
+  GdkWindow *event_window;
   guint draw : 1;
 };
 
@@ -59,20 +59,28 @@ enum {
 
 static gboolean gtk_separator_tool_item_create_menu_proxy (GtkToolItem               *item);
 static void     gtk_separator_tool_item_set_property      (GObject                   *object,
-                                                          guint                      prop_id,
-                                                          const GValue              *value,
-                                                          GParamSpec                *pspec);
-static void     gtk_separator_tool_item_get_property       (GObject                   *object,
-                                                          guint                      prop_id,
-                                                          GValue                    *value,
-                                                          GParamSpec                *pspec);
+                                                           guint                      prop_id,
+                                                           const GValue              *value,
+                                                           GParamSpec                *pspec);
+static void     gtk_separator_tool_item_get_property      (GObject                   *object,
+                                                           guint                      prop_id,
+                                                           GValue                    *value,
+                                                           GParamSpec                *pspec);
 static void     gtk_separator_tool_item_size_request      (GtkWidget                 *widget,
-                                                          GtkRequisition            *requisition);
+                                                           GtkRequisition            *requisition);
+static void     gtk_separator_tool_item_size_allocate     (GtkWidget                 *widget,
+                                                           GtkAllocation             *allocation);
 static gboolean gtk_separator_tool_item_expose            (GtkWidget                 *widget,
-                                                          GdkEventExpose            *event);
+                                                           GdkEventExpose            *event);
 static void     gtk_separator_tool_item_add               (GtkContainer              *container,
-                                                          GtkWidget                 *child);
+                                                           GtkWidget                 *child);
 static gint     get_space_size                            (GtkToolItem               *tool_item);
+static void     gtk_separator_tool_item_realize           (GtkWidget                 *widget);
+static void     gtk_separator_tool_item_unrealize         (GtkWidget                 *widget);
+static void     gtk_separator_tool_item_map               (GtkWidget                 *widget);
+static void     gtk_separator_tool_item_unmap             (GtkWidget                 *widget);
+static gboolean gtk_separator_tool_item_button_event      (GtkWidget                 *widget,
+                                                           GdkEventButton            *event);
 
 
 G_DEFINE_TYPE (GtkSeparatorToolItem, gtk_separator_tool_item, GTK_TYPE_TOOL_ITEM)
@@ -86,8 +94,8 @@ get_space_size (GtkToolItem *tool_item)
   if (GTK_IS_TOOLBAR (parent))
     {
       gtk_widget_style_get (parent,
-                           "space-size", &space_size,
-                           NULL);
+                            "space-size", &space_size,
+                            NULL);
     }
   
   return space_size;
@@ -109,34 +117,45 @@ gtk_separator_tool_item_class_init (GtkSeparatorToolItemClass *class)
   object_class->set_property = gtk_separator_tool_item_set_property;
   object_class->get_property = gtk_separator_tool_item_get_property;
   widget_class->size_request = gtk_separator_tool_item_size_request;
+  widget_class->size_allocate = gtk_separator_tool_item_size_allocate;
   widget_class->expose_event = gtk_separator_tool_item_expose;
+  widget_class->realize = gtk_separator_tool_item_realize;
+  widget_class->unrealize = gtk_separator_tool_item_unrealize;
+  widget_class->map = gtk_separator_tool_item_map;
+  widget_class->unmap = gtk_separator_tool_item_unmap;
+  widget_class->button_press_event = gtk_separator_tool_item_button_event;
+  widget_class->button_release_event = gtk_separator_tool_item_button_event;
+
   toolitem_class->create_menu_proxy = gtk_separator_tool_item_create_menu_proxy;
   
   container_class->add = gtk_separator_tool_item_add;
   
   g_object_class_install_property (object_class,
-                                  PROP_DRAW,
-                                  g_param_spec_boolean ("draw",
-                                                        P_("Draw"),
-                                                        P_("Whether the separator is drawn, or just blank"),
-                                                        TRUE,
-                                                        GTK_PARAM_READWRITE));
+                                   PROP_DRAW,
+                                   g_param_spec_boolean ("draw",
+                                                         P_("Draw"),
+                                                         P_("Whether the separator is drawn, or just blank"),
+                                                         TRUE,
+                                                         GTK_PARAM_READWRITE));
   
+
   g_type_class_add_private (object_class, sizeof (GtkSeparatorToolItemPrivate));
 }
 
 static void
-gtk_separator_tool_item_init (GtkSeparatorToolItem      *separator_item)
+gtk_separator_tool_item_init (GtkSeparatorToolItem *separator_item)
 {
   separator_item->priv = G_TYPE_INSTANCE_GET_PRIVATE (separator_item,
                                                       GTK_TYPE_SEPARATOR_TOOL_ITEM,
                                                       GtkSeparatorToolItemPrivate);
   separator_item->priv->draw = TRUE;
+
+  gtk_widget_set_has_window (GTK_WIDGET (separator_item), FALSE);
 }
 
 static void
 gtk_separator_tool_item_add (GtkContainer *container,
-                            GtkWidget    *child)
+                             GtkWidget    *child)
 {
   g_warning ("attempt to add a child to an GtkSeparatorToolItem");
 }
@@ -155,9 +174,9 @@ gtk_separator_tool_item_create_menu_proxy (GtkToolItem *item)
 
 static void
 gtk_separator_tool_item_set_property (GObject      *object,
-                                     guint         prop_id,
-                                     const GValue *value,
-                                     GParamSpec   *pspec)
+                                      guint         prop_id,
+                                      const GValue *value,
+                                      GParamSpec   *pspec)
 {
   GtkSeparatorToolItem *item = GTK_SEPARATOR_TOOL_ITEM (object);
   
@@ -174,9 +193,9 @@ gtk_separator_tool_item_set_property (GObject      *object,
 
 static void
 gtk_separator_tool_item_get_property (GObject      *object,
-                                     guint         prop_id,
-                                     GValue       *value,
-                                     GParamSpec   *pspec)
+                                      guint         prop_id,
+                                      GValue       *value,
+                                      GParamSpec   *pspec)
 {
   GtkSeparatorToolItem *item = GTK_SEPARATOR_TOOL_ITEM (object);
   
@@ -193,7 +212,7 @@ gtk_separator_tool_item_get_property (GObject      *object,
 
 static void
 gtk_separator_tool_item_size_request (GtkWidget      *widget,
-                                     GtkRequisition *requisition)
+                                      GtkRequisition *requisition)
 {
   GtkToolItem *item = GTK_TOOL_ITEM (widget);
   GtkOrientation orientation = gtk_tool_item_get_orientation (item);
@@ -210,9 +229,119 @@ gtk_separator_tool_item_size_request (GtkWidget      *widget,
     }
 }
 
+static void
+gtk_separator_tool_item_size_allocate (GtkWidget     *widget,
+                                       GtkAllocation *allocation)
+{
+  GtkSeparatorToolItem *separator = GTK_SEPARATOR_TOOL_ITEM (widget);
+  GtkSeparatorToolItemPrivate *priv = separator->priv;
+
+  widget->allocation = *allocation;
+
+  if (gtk_widget_get_realized (widget))
+    gdk_window_move_resize (priv->event_window,
+                            widget->allocation.x,
+                            widget->allocation.y,
+                            widget->allocation.width,
+                            widget->allocation.height);
+
+}
+
+static void
+gtk_separator_tool_item_realize (GtkWidget *widget)
+{
+  GtkSeparatorToolItem *separator = GTK_SEPARATOR_TOOL_ITEM (widget);
+  GtkSeparatorToolItemPrivate *priv = separator->priv;
+  GdkWindowAttr attributes;
+  gint attributes_mask;
+
+  gtk_widget_set_realized (widget, TRUE);
+
+  attributes.window_type = GDK_WINDOW_CHILD;
+  attributes.x = widget->allocation.x;
+  attributes.y = widget->allocation.y;
+  attributes.width = widget->allocation.width;
+  attributes.height = widget->allocation.height;
+  attributes.wclass = GDK_INPUT_ONLY;
+  attributes.visual = gtk_widget_get_visual (widget);
+  attributes.colormap = gtk_widget_get_colormap (widget);
+  attributes.event_mask = gtk_widget_get_events (widget) |
+                          GDK_BUTTON_PRESS_MASK |
+                          GDK_BUTTON_RELEASE_MASK;
+  attributes_mask = GDK_WA_X | GDK_WA_Y;
+
+  widget->window = gtk_widget_get_parent_window (widget);
+  g_object_ref (widget->window);
+
+  priv->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
+                                       &attributes, attributes_mask);
+  gdk_window_set_user_data (priv->event_window, widget);
+
+  widget->style = gtk_style_attach (widget->style, widget->window);
+}
+
+static void
+gtk_separator_tool_item_unrealize (GtkWidget *widget)
+{
+  GtkSeparatorToolItem *separator = GTK_SEPARATOR_TOOL_ITEM (widget);
+  GtkSeparatorToolItemPrivate *priv = separator->priv;
+
+  if (priv->event_window)
+    {
+      gdk_window_set_user_data (priv->event_window, NULL);
+      gdk_window_destroy (priv->event_window);
+      priv->event_window = NULL;
+    }
+
+  GTK_WIDGET_CLASS (gtk_separator_tool_item_parent_class)->unrealize (widget);
+}
+
+static void
+gtk_separator_tool_item_map (GtkWidget *widget)
+{
+  GtkSeparatorToolItem *separator = GTK_SEPARATOR_TOOL_ITEM (widget);
+  GtkSeparatorToolItemPrivate *priv = separator->priv;
+
+  GTK_WIDGET_CLASS (gtk_separator_tool_item_parent_class)->map (widget);
+
+  if (priv->event_window)
+    gdk_window_show (priv->event_window);
+}
+
+static void
+gtk_separator_tool_item_unmap (GtkWidget *widget)
+{
+  GtkSeparatorToolItem *separator = GTK_SEPARATOR_TOOL_ITEM (widget);
+  GtkSeparatorToolItemPrivate *priv = separator->priv;
+
+  if (priv->event_window)
+    gdk_window_hide (priv->event_window);
+
+  GTK_WIDGET_CLASS (gtk_separator_tool_item_parent_class)->unmap (widget);
+}
+
+static gboolean
+gtk_separator_tool_item_button_event (GtkWidget      *widget,
+                                      GdkEventButton *event)
+{
+  GtkSeparatorToolItem *separator = GTK_SEPARATOR_TOOL_ITEM (widget);
+  GtkSeparatorToolItemPrivate *priv = separator->priv;
+
+  /* We want window dragging to work on empty toolbar areas,
+   * so we only eat button events on visible separators
+   */
+  return priv->draw;
+}
+
+#define DEFAULT_SPACE_SIZE  12
+#define DEFAULT_SPACE_STYLE GTK_TOOLBAR_SPACE_LINE
+#define SPACE_LINE_DIVISION 10.0
+#define SPACE_LINE_START    2.0
+#define SPACE_LINE_END      8.0
+
 static gboolean
 gtk_separator_tool_item_expose (GtkWidget      *widget,
-                               GdkEventExpose *event)
+                                GdkEventExpose *event)
 {
   GtkToolbar *toolbar = NULL;
   GtkSeparatorToolItem *separator = GTK_SEPARATOR_TOOL_ITEM (widget);
@@ -221,12 +350,12 @@ gtk_separator_tool_item_expose (GtkWidget      *widget,
   if (priv->draw)
     {
       if (GTK_IS_TOOLBAR (widget->parent))
-       toolbar = GTK_TOOLBAR (widget->parent);
+        toolbar = GTK_TOOLBAR (widget->parent);
 
       _gtk_toolbar_paint_space_line (widget, toolbar,
-                                    &(event->area), &widget->allocation);
+                                     &(event->area), &widget->allocation);
     }
-  
+
   return FALSE;
 }
 
@@ -245,7 +374,7 @@ gtk_separator_tool_item_new (void)
   GtkToolItem *self;
   
   self = g_object_new (GTK_TYPE_SEPARATOR_TOOL_ITEM,
-                      NULL);
+                       NULL);
   
   return self;
 }
@@ -282,7 +411,7 @@ gtk_separator_tool_item_get_draw (GtkSeparatorToolItem *item)
  */
 void
 gtk_separator_tool_item_set_draw (GtkSeparatorToolItem *item,
-                                 gboolean              draw)
+                                  gboolean              draw)
 {
   g_return_if_fail (GTK_IS_SEPARATOR_TOOL_ITEM (item));
 
index a010ef9ef5b56554df7bd04997907d2e1fea543b..d5a5afcf1604d103aecf89e7250bdcb48a29bacb 100644 (file)
@@ -2630,17 +2630,41 @@ static gboolean
 gtk_toolbar_button_press (GtkWidget      *toolbar,
                          GdkEventButton *event)
 {
+  GtkWidget *window;
+
   if (event->button == 3)
     {
       gboolean return_value;
-      
+
       g_signal_emit (toolbar, toolbar_signals[POPUP_CONTEXT_MENU], 0,
                     (int)event->x_root, (int)event->y_root, event->button,
                     &return_value);
-      
+
       return return_value;
     }
-  
+
+  window = gtk_widget_get_toplevel (toolbar);
+
+  if (window)
+    {
+      gboolean window_drag = FALSE;
+
+      gtk_widget_style_get (toolbar,
+                            "window-dragging", &window_drag,
+                            NULL);
+
+      if (window_drag)
+        {
+          gtk_window_begin_move_drag (GTK_WINDOW (window),
+                                      event->button,
+                                      event->x_root,
+                                      event->y_root,
+                                      event->time);
+
+          return TRUE;
+        }
+    }
+
   return FALSE;
 }
 
index b092857b358f7e4210a0e09af0500b5bdc17b314..f65b7aecaf3f6e5110842a78bd46781c1db7b398 100644 (file)
@@ -2491,6 +2491,13 @@ gtk_widget_class_init (GtkWidgetClass *klass)
                                                               0.0, 1.0, 0.04,
                                                               GTK_PARAM_READABLE));
 
+  gtk_widget_class_install_style_property (klass,
+                                           g_param_spec_boolean ("window-dragging",
+                                                                 P_("Window dragging"),
+                                                                 P_("Whether windows can be dragged by clicking on empty areas"),
+                                                                 FALSE,
+                                                                 GTK_PARAM_READWRITE));
+
   /**
    * GtkWidget:draw-border:
    *